﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace Smart.Common
{
	[Localizable(false), DebuggerStepThrough]
	public static class SPListItemExts
	{
		#region Extensions with string fieldName
		public static object Get(this SPListItem item, string fieldName)
		{
			object res = null;

			try
			{
				if (item != null) res = item[fieldName];
			}
			catch (Exception x)
			{
				H.LX("SPListItemExts.Get", String.Concat("SPListItemExts.Get (", (item == null ? "null" : item.ParentList.Title), ", ", (item == null ? "null" : item.ID.ToString(H.En)), ", ", fieldName, ")"), x);
			}

			return res;
		}

		public static TValueType Get<TValueType>(this SPListItem item, string fieldName, TValueType defaultValue)
		{
			var tmp = Get(item, fieldName);

			return tmp == null ? defaultValue : (TValueType)tmp;
		}

		public static string GetStr(this SPListItem item, string fieldName)
		{
			return Get(item, fieldName).SToStr(null);
		}

		public static int GetInt(this SPListItem item, string fieldName)
		{
			return Get(item, fieldName).SToInt(0);
		}

		public static double GetDbl(this SPListItem item, string fieldName)
		{
			return Get(item, fieldName).SToDbl(0);
		}

		public static bool GetBool(this SPListItem item, string fieldName)
		{
			return Get(item, fieldName).SToBool(false);
		}

		public static DateTime GetDT(this SPListItem item, string fieldName)
		{
			return Get(item, fieldName).SToDT(DateTime.MinValue);
		}

		public static SPFieldLookupValue GetLookup(this SPListItem item, string fieldName)
		{
			return new SPFieldLookupValue(GetStr(item, fieldName));
		}

		public static SPFieldLookupValueCollection GetMultiLookup(this SPListItem item, string fieldName)
		{
			return new SPFieldLookupValueCollection(GetStr(item, fieldName));
		}

		public static int GetLookupId(this SPListItem item, string fieldName)
		{
			return GetLookup(item, fieldName).LookupId;
		}

		public static string GetLookupTitle(this SPListItem item, string fieldName)
		{
			return GetLookup(item, fieldName).LookupValue;
		}

		public static bool GetLookupIsEmpty(this SPListItem item, string fieldName)
		{
			var v = GetStr(item, fieldName);

			if (string.IsNullOrEmpty(v))
				return true;

			int id;
			string title;

			SPH.DispatchLookupValue(v, out id, out title);

			return id <= 0;
		}

		public static object GetFormulaValue(this SPListItem item, string fieldName)
		{
			return SPH.GetFormulaValue(GetStr(item, fieldName));
		}

		public static SPFieldUserValue GetUser(this SPListItem item, string fieldName)
		{
			var fieldValue = GetStr(item, fieldName);

			return string.IsNullOrEmpty(fieldValue) ? null : new SPFieldUserValue(item.Web, fieldValue);
		}

		public static SPFieldUserValueCollection GetUsers(this SPListItem item, string fieldName)
		{
			var fieldValue = GetStr(item, fieldName);

			return string.IsNullOrEmpty(fieldValue) ? null : new SPFieldUserValueCollection(item.Web, fieldValue);
		}

		public static bool GetContainsPrincipal(this SPListItem item, string fieldName, int principalId)
		{
			var users = GetMultiLookup(item, fieldName);

			if (users == null)
				return false;

			// ReSharper disable LoopCanBeConvertedToQuery
			foreach (var lookupValue in users)
			{
				if (lookupValue != null && lookupValue.LookupId == principalId)
					return true;
			}
			// ReSharper restore LoopCanBeConvertedToQuery

			return false;
		}

		public static SPListItem Set(this SPListItem item, string fieldName, object value)
		{
			try
			{
				if (item != null) item[fieldName] = value;
			}
			catch (Exception x)
			{
				H.LX("SPListItemExts.Set", String.Concat("SPListItemExts.Set (", item == null ? "null" : item.ParentList.Title, ", ", item == null ? "null" : item.ID.ToString(H.En), ", ", fieldName, ", ", value == null ? "null" : value.ToString(), ")"), x);
			}

			return item;
		}
		#endregion

		#region Extensions with Guid fieldId
		public static object Get(this SPListItem item, Guid fieldId)
		{
			object res = null;

			try
			{
				if (item != null) res = item[fieldId];
			}
			catch (Exception x)
			{
				H.LX("SPListItemExts.Get", String.Concat("SPListItemExts.Get (", (item == null ? "null" : item.ParentList.Title), ", ", (item == null ? "null" : item.ID.ToString(H.En)), ", ", fieldId, ")"), x);
			}

			return res;
		}

		public static TValueType Get<TValueType>(this SPListItem item, Guid fieldId, TValueType defaultValue)
		{
			var tmp = Get(item, fieldId);

			return tmp == null ? defaultValue : (TValueType)tmp;
		}

		public static string GetStr(this SPListItem item, Guid fieldId)
		{
			return Get(item, fieldId).SToStr(null);
		}

		public static int GetInt(this SPListItem item, Guid fieldId)
		{
			return Get(item, fieldId).SToInt(0);
		}

		public static double GetDbl(this SPListItem item, Guid fieldId)
		{
			return Get(item, fieldId).SToDbl(0);
		}

		public static bool GetBool(this SPListItem item, Guid fieldId)
		{
			return Get(item, fieldId).SToBool(false);
		}

		public static DateTime GetDT(this SPListItem item, Guid fieldId)
		{
			return Get(item, fieldId).SToDT(DateTime.MinValue);
		}

		public static SPFieldLookupValue GetLookup(this SPListItem item, Guid fieldId)
		{
			return new SPFieldLookupValue(GetStr(item, fieldId));
		}

		public static SPFieldLookupValueCollection GetMultiLookup(this SPListItem item, Guid fieldId)
		{
			return new SPFieldLookupValueCollection(GetStr(item, fieldId));
		}

		public static int GetLookupId(this SPListItem item, Guid fieldId)
		{
			return GetLookup(item, fieldId).LookupId;
		}

		public static string GetLookupTitle(this SPListItem item, Guid fieldId)
		{
			return GetLookup(item, fieldId).LookupValue;
		}

		public static bool GetLookupIsEmpty(this SPListItem item, Guid fieldId)
		{
			var v = GetStr(item, fieldId);

			if (string.IsNullOrEmpty(v))
				return true;

			int id;
			string title;

			SPH.DispatchLookupValue(v, out id, out title);

			return id <= 0;
		}

		public static object GetFormulaValue(this SPListItem item, Guid fieldId)
		{
			return SPH.GetFormulaValue(GetStr(item, fieldId));
		}

		public static SPFieldUserValue GetUser(this SPListItem item, Guid fieldId)
		{
			var fieldValue = GetStr(item, fieldId);

			return string.IsNullOrEmpty(fieldValue) ? null : new SPFieldUserValue(item.Web, fieldValue);
		}

		public static SPFieldUserValueCollection GetUsers(this SPListItem item, Guid fieldId)
		{
			var fieldValue = GetStr(item, fieldId);

			return string.IsNullOrEmpty(fieldValue) ? null : new SPFieldUserValueCollection(item.Web, fieldValue);
		}

		public static bool GetContainsPrincipal(this SPListItem item, Guid fieldId, int principalId)
		{
			var users = GetMultiLookup(item, fieldId);

			if (users == null)
				return false;

			// ReSharper disable LoopCanBeConvertedToQuery
			foreach (var lookupValue in users)
			{
				if (lookupValue != null && lookupValue.LookupId == principalId)
					return true;
			}
			// ReSharper restore LoopCanBeConvertedToQuery

			return false;
		}

		public static SPListItem Set(this SPListItem item, Guid fieldId, object value)
		{
			try
			{
				if (item != null) item[fieldId] = value;
			}
			catch (Exception x)
			{
				H.LX("SPListItemExts.Set", String.Concat("SPListItemExts.Set (", item == null ? "null" : item.ParentList.Title, ", ", item == null ? "null" : item.ID.ToString(H.En), ", ", fieldId, ", ", value == null ? "null" : value.ToString(), ")"), x);
			}

			return item;
		}
		#endregion

		public static string FormatLookupValue(this SPListItem item)
		{
			return SPH.FormatLookupValue(item.ID, item.Title);
		}

		/// <summary>
		/// Return item field values
		/// </summary>
		public static void GetValues(this SPListItem item, IDictionary<string, object> values)
		{
			item.GetValues(values, null);
		}

		/// <summary>
		/// Return item field values
		/// </summary>
		public static void GetValues(this SPListItem item, IDictionary<string, object> values, Action<SPListItem, IDictionary<string, object>, SPField> fillUnknownValue)
		{
			if (item == null)
				return;

			var web = item.Web;

			foreach (SPField field in item.Fields)
			{
				try
				{
					string tmp;

					switch (field.Type)
					{
						case SPFieldType.Counter:
						case SPFieldType.Integer:
							values[field.InternalName] = item.GetInt(field.InternalName);
							break;
						case SPFieldType.Note:
						case SPFieldType.Text:
						case SPFieldType.Choice:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.DateTime:
							var dt = item.GetDT(field.InternalName);
							values[field.InternalName] = dt.IsEmpty() ? (object)null : dt;
							break;
						case SPFieldType.Lookup:
							var lfield = (SPFieldLookup)field;

							tmp = item.GetStr(field.InternalName);

							values[field.InternalName] =
								!lfield.IsRelationship || tmp.IsNullOrEmpty()
									? tmp
									: lfield.AllowMultipleValues
										  ? (object)new SPFieldLookupValueCollection(tmp)
										  : new SPFieldLookupValue(tmp);
							break;
						case SPFieldType.Boolean:
							values[field.InternalName] = item.GetBool(field.InternalName);
							break;
						case SPFieldType.Currency:
						case SPFieldType.Number:
							values[field.InternalName] = item.GetDbl(field.InternalName);
							break;
						case SPFieldType.URL:
							tmp = item.GetStr(field.InternalName);
							values[field.InternalName] = tmp.IsNullOrEmpty() ? null : new SPFieldUrlValue(tmp);
							break;
						case SPFieldType.Calculated:
						case SPFieldType.Computed:
							values[field.InternalName] = (item.GetStr(field.InternalName).GetPostPartDelimitValue(SPH.LookupValueDelimiter) ?? String.Empty).Trim();
							break;
						case SPFieldType.Guid:
							var g = item.GetStr(field.InternalName).SToGuid();
							values[field.InternalName] = g.IsEmpty() ? (object)null : g;
							break;
						case SPFieldType.GridChoice:
						case SPFieldType.MultiChoice:
							tmp = item.GetStr(field.InternalName);
							values[field.InternalName] = tmp.IsNullOrEmpty() ? null : new SPFieldMultiChoiceValue(tmp);
							break;
						case SPFieldType.Attachments:
						case SPFieldType.File:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.User:
							var ufield = (SPFieldUser)field;

							tmp = item.GetStr(field.InternalName);

							values[field.InternalName] =
								tmp.IsNullOrEmpty()
									? null
									: ufield.AllowMultipleValues
										  ? (object)new SPFieldUserValueCollection(web, tmp)
										  : new SPFieldUserValue(web, tmp);
							break;
						case SPFieldType.Recurrence:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.CrossProjectLink:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.ModStat:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.Error:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.ContentTypeId:
							tmp = item.GetStr(field.InternalName);
							values[field.InternalName] = tmp.IsNullOrEmpty() ? SPContentTypeId.Empty : new SPContentTypeId(tmp);
							break;
						case SPFieldType.PageSeparator:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.ThreadIndex:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.WorkflowStatus:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.AllDayEvent:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.WorkflowEventType:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.MaxItems:
							values[field.InternalName] = item.GetStr(field.InternalName);
							break;
						case SPFieldType.Invalid:
							switch (field.TypeAsString.ToLower())
							{
								case "lookupfieldwithpicker":
								case "lookupfieldwithpickermulti":
									var lpfield = (SPFieldLookup)field;

									tmp = item.GetStr(field.InternalName);

									values[field.InternalName] =
										!lpfield.IsRelationship || tmp.IsNullOrEmpty()
											? tmp
											: lpfield.AllowMultipleValues
												  ? (object)new SPFieldLookupValueCollection(tmp)
												  : new SPFieldLookupValue(tmp);
									break;
								default:
									if (fillUnknownValue != null)
										fillUnknownValue(item, values, field);
									else
										H.LT("GetFieldValues unknown field type: {0}", field.TypeAsString);
									break;
							}
							// do nothing
							break;
						default:
							if (fillUnknownValue != null)
								fillUnknownValue(item, values, field);
							else
								H.LT("GetFieldValues unknown field type: {0}", field.Type);
							break;
					}
				}
				catch (Exception x)
				{
					H.LX("GetFieldValues", x);
				}
			}
		}

		/// <summary>
		/// Return DocumentSet home page or list item dispform url
		/// </summary>
		public static string GetItemDispUrl(this SPListItem item)
		{
			var mw = MetaCtx.GetWeb(item.Web);

			return
				item.ContentTypeId.IsChildOrEqual(SPBuiltInContentTypeId.DocumentSet)
					? SPH.GetDocumentSetHomePageUrl(mw.Url, item.Folder.ServerRelativeUrl)
					: SPH.GetDispFormUrl(mw.Url, item.ParentList.ID, item.ID, null, false);
		}
	}
}
